// Text of project SoupDrink written on 1/8/96 at 2:26 PM
// Beginning of file Main.t

// Before Script for "DILSample"
// By Rob Langhorne, Christopher Bell, David Fedor, and Jim Schram
// Copyright Apple Computer, Inc. 1995-1996.  All rights reserved.


DILSample :=
    {
     CreateNBPAddress:
       func(address)
       begin	//* create an ADSP endpoint address option frame and return it
       
       	if address then
       		if IsFrame(address) then
       			return(TotalClone(address));
       		else  // probably a string
       			return {			
       				label:	kCMARouteLabel,
       				type:		'address,
       				opCode:	opSetRequired,
       				data:		{
       							addressType: kNamedAppleTalkAddress,
       							addressData: TotalClone(address), 
       							},
       			}
       	else
       		return nil
       
       end,
     viewSetupDoneScript:
       func()
       begin
       	// make a RAM copy here so we can modify it & access methods in base App
       	ep := {	_proto:		baseEndpoint,
       			_parent:	self,
       			appBase:	self,	};
       end,
     GoDrink:
       func(s)
       begin
       	local theStr;
       	local theNames;
       
       	if (BeginsWith(s, "OK")) then begin
       		local theEntry := theCursor:Entry();
       
       		if theEntry then
       			begin
       				ep:Output("ENTR", frameOptions);	// tell the server we are sending a soup entry
       				ep:FlushOutput();
       
       				if spitMode then begin  			// if we are only sending strings because of ADSP OutputFrame bug
       					theStr := "";
       					if isframe(theEntry.Name) then begin
       						if theEntry.name.honorific exists then
       							theStr := theEntry.name.honorific & " ";
       						if theEntry.name.first exists then
       							theStr := theEntry.name.first & " ";
       						if theEntry.name.last exists then
       							theStr := theStr & theEntry.name.last;
       					end;
       					TrimString(theStr);				// remove any extra white space
       					if StrLen(theStr) = 0 then 
       						thestr:="<no name in this record>";
       
       					// allocate 4-byte object
       					local lengthObj := setclass(setlength(clone(""), 4), 'binary);
       					StuffLong(lengthObj, 0, strlen(theStr)); // turn the string length into a 4byte int
       					
       					// output string length as 4 byte. as normal int, output sends only a one-byte int
       					ep:Output(lengthObj, frameOptions);
       					ep:Output(theStr, frameOptions);
       				end else							// we're sending frames; just send the whole thing over.
       					ep:OutputFrame(theEntry, frameOptions);
       
       				ep:FlushOutput();
       		
       				if theEntry then
       					frameCount := frameCount + 1;		//	increment counter
       	      
       				:PutDataInStatusArea("Frame Sent: " && NumberStr(frameCount));
       						
       				theEntry := theCursor:Next();
       				ep:SetInputSpec(ep.doEntry);	// this shouldn't be necessary but works around a Newton 2.0 bug-ette
       			end;
       		else begin
       			ep:Output("END ", frameOptions); // tell the server we are done
       			ep:FlushOutput(); 
       			:PutDataInStatusArea("All Frames Sent");
       			:DisposeConnection();
       		end;
       	end else begin
       		:PutDataInStatusArea("Unknown Command Received...");
       		ep:SetInputSpec(ep.doInput);
       	end;
       end,
     ep: nil,
     InitConnection:
       func(passive)
       begin
       	local address := nil;
       
       	:PutDataInStatusArea("Trying to connect...");
       	RefreshViews();
       	
       	ep.configOptions := configOptions.(endpointType);
       	
       	local err;
       	try
       		err := ep:Instantiate(ep, nil)
       	onexception |evt.ex| do
       		err := -666;
       	
       	if err then
       		begin
       			:Notify(kNotifyAlert, EnsureInternal("SoupDrink"), EnsureInternal("Sorry, can't create an endpoint!"));
       			:PutDataInStatusArea("Unable to initialize...");
       			if endpointType = 'adsp then CloseAppleTalk();
       			return;
       		end;
       
       	if endpointType = 'adsp then
       		address := NBPaddress;		// this was filled in by the chooser
       
       	:MakeConnection(passive, address);
       end,
     theSoup: nil,
     viewQuitScript:
       func()
       begin
       RemovePowerOffHandler(self);
       end,
     frameCount: 0,
     SetEndpointType:
       func(t)
       begin
       	endpointType := t;
       end,
     compileTime:
       time(); // this will evaluate to the compile time
       ,
     NetworkChooserDone:
       func(selection, zone)
       begin
       	if selection then
       		begin
       			local passive := nil;
       			local address := selection & ":" & vNBPtype & "@" & if zone then zone else "*";	//* convert selection into an NBP address
       			NBPaddress := :CreateNBPAddress(address);
       			:PutDataInStatusArea("About to Connect...");
       			:InitConnection(passive);
       		end;
       	else
       		:PutDataInStatusArea("Ready to connect...(cancelled chooser)");
       end,
     Connected: nil,
     baseEndpoint:
       {
       	_proto: protoEndpoint,
       
       	configOptions:  nil,  // overridden by each endpoint type
       
       	appBase: nil,
       
       	exceptionHandler: func(exception)
       		begin
       			if (exception.data <> -16005 and exception.data <> -38001) then
       				GetRoot():Notify(kNotifyAlert, EnsureInternal("SoupDrink"), EnsureInternal("Exception: " & exception.data));
       			true;
       		end,
       
          doInput:
             {
       		inputForm:		'string,
       		endCharacter:	unicodeEOT,
       		discardAfter:	256,
       
       		InputScript : func(endpoint, s)
       		begin
                   endpoint:PutDataInStatusArea("Command Received...");
                   endpoint.appBase:StartDrink(s);
       		end,
             },
       
          doEntry:
             {
       		inputForm:		'string,
       		endCharacter:	unicodeEOT,
       		discardAfter:	256,
       
       		InputScript : func(endpoint, s)
       		begin
                   endpoint.appBase:GoDrink(s);
       		end,
             },
       
       	doFrame:
       		{
             	inputForm: 'frame ,
       
             	InputScript: func(endpoint, f)
                	begin
                   	endpoint:output("OK",endpoint.appBase.frameOptions);		// tell the other side we got it; they can disconnect.
       				endpoint.appBase:AddName(f);
               	end,
       		},
       },
     PutDataInStatusArea:
       func(s)
       begin
          SetValue(StatusArea, 'text, s);
          AddArraySlot(StatusMessages, s);		// keep the messages around for debugging purposes
       end,
     configOptions:
       {		serial:   [		// serial tool
       			 { label: 	"aser", type: 'service, opCode: opSetRequired },
       
       			 { label: kCMOSerialIOParms, type: 'option, opCode: opSetNegotiate,
       				  data: { bps: k38400bps, dataBits: k8DataBits, stopBits: k1StopBits, parity: kNoParity } },
       		],
       		mnp:   [ 		//	MNP
       			 { label: 	kCMSMNPID, type: 'service, opCode: opSetRequired },
       
       			 { label: kCMOSerialIOParms, type: 'option, opCode: opSetNegotiate,
       				  data:
                 		{ bps: k38400bps, dataBits: k8DataBits, stopBits: k1StopBits, parity: kNoParity } 
           		 		},
       			 { label: kCMOMNPCompression, type: 'option, opCode: opSetRequired, data: kMNPCompressionV42bis},
       			 { label: kCMOMNPAllocate, type: 'option, opCode: opSetRequired, data: kMNPDoAllocate},
       			 { label: kCMOMNPDataRate, type: 'option, opCode: opSetRequired, data: k38400bps},
       		],
       		modemMNP:   [		// modem mnp tool
       			 { label: 	kCMSModemID, type: 'service, opCode: opSetRequired },
       			 { label: kCMOModemECType, type: 'option, opCode: opSetRequired,
       				  data: kModemECProtocolMNP },
       			 { label: kCMOMNPAllocate, type: 'option, opCode: opSetRequired,
       				  data: kMNPDoAllocate },
       			 { label: kCMOMNPCompression, type: 'option, opCode: opSetRequired,
       				  data: kMNPCompressionMNP5 },
       		],
       		adsp:   [		// adsp (AppleTalk) tool
       		   {   label:   kCMSAppleTalkID,
       		       type:    'service,
       		       opCode:  opSetRequired },
       		
       		   {   label:   kCMSAppleTalkID,
       		       type:    'option,
       		       opCode:  opSetRequired,
       		       data:    kCMOAppleTalkADSP },
       		   
       		   {   label:   kCMOEndpointName,
       		       type:    'option,
       		       opCode:  opSetRequired,
       		       data:    kADSPEndpoint },
       		   ],
       		};,
     StartDrink:
       func(s)
       begin
       	local theStr, theNames; 
       
       	frameCount := 0;
       	if (BeginsWith(s, "DRNK")) then 
       			begin
       
       			// Because of problems with ADSP and OutputFrame in unpatched 1.x Newton devices, we 
       			// sense when we cannot use ADSP frames.  This bug was fixed in the December '95 patches.
       			if (endpointType = 'adsp) and (SendADSPFrames.viewValue = nil) then begin
       				:PutDataInStatusArea("ADSP OutputFrame not supported..." && soupName);
       				spitMode := true;
       				ep:Output("SPIT", frameOptions); // tell the server we can only spit, not drink
       			end else begin
       				spitMode := nil;
       				ep:Output("DRNK", frameOptions); // tell the server we can do a drink
       			end;
       
       			ep:FlushOutput();
       
       			local soupName := SubStr(s, 4, strLen(s) - 5 );
       
       			theSoup := GetUnionSoup(soupName);
       
       			if NOT theSoup then
       				:PutDataInStatusArea("Unknown Soup Requested..." && soupName);
       			else begin
       				:PutDataInStatusArea("Open Soup: " && soupName);
        				local queryType := {type: 'index};
           
          				theCursor := Query(theSoup, queryType);
       				local theEntry := theCursor:Entry();
       
       				ep:Output("ENTR", frameOptions); // tell the server we are sending a soup entry
       				ep:FlushOutput();
       
       				if spitMode then begin 	/* if we are only sending strings */
       					theStr := "";
       					if isframe(theEntry.Name) then begin
       						if theEntry.name.honorific exists then
       							theStr := theEntry.name.honorific & " ";
       						if theEntry.name.first exists then
       							theStr := theEntry.name.first & " ";
       						if theEntry.name.last exists then
       							theStr := theStr & theEntry.name.last;
       					end;
       					TrimString(theStr);				// remove any extra white space
       					if StrLen(theStr) = 0 then 
       						thestr:="<no name in this record>";
       
       					// allocate 4-byte object to hold the length
       					local lengthObj := setclass(setlength(clone(""), 4), 'binary);
       					StuffLong(lengthObj, 0, strlen(theStr)); // turn the string length into a 4byte int
       					
       					// output string length as 4 byte int; output sends only a one-byte int
       					ep:Output(lengthObj, frameOptions);
       					ep:Output(theStr, frameOptions);
       					end;
       				else					/* if we are sending frames */
       					ep:OutputFrame(theEntry, frameOptions);
       
       				ep:FlushOutput();
       
       				frameCount := frameCount + 1;		//	increment counter
          				:PutDataInStatusArea("Frame Sent: " && NumberStr(frameCount));
       				
       				theEntry := theCursor:Next();
       				ep:SetInputSpec(ep.doEntry);
       			end;
       		end;
       	else if (BeginsWith(s, "NAME")) then begin
       				theSoup := GetUnionSoup("Names");
       			   if NOT theSoup then begin
       					:PutDataInStatusArea("Names soup doesn't exist (?!?)...");
       					refreshviews();
       					:DisposeConnection();
       				end else begin
       					:PutDataInStatusArea("Open Names Soup");
       					refreshviews();
       					ep:SetInputSpec(ep.doFrame);
       				end;
       		end;
       	else if (BeginsWith(s, "HELO")) then begin	// this is used by the DILette sample... just respond "Hello" and disconnect.
       			ep:Output("Hello", frameOptions);
       			ep:FlushOutput();
       			GetRoot():Notify(knotifyAlert, clone("SoupDrink"), clone("Hello, DILette... pleased to meet you!"));
       			:DisposeConnection();
       		end;
       	else
             	:PutDataInStatusArea("Unknown Command:" && s);
       end,
     viewBounds: {top: 0, left: 0, right: 240, bottom: 330},
     endpointType: nil,
     _proto: @157,
     DisposeConnection:
       func()
       begin
       	:PutDataInStatusArea("Disconnecting...");
       	:ReleaseConnection();
       	:PutDataInStatusArea("Ready to connect...");
       	ConnectButton:Show();
       end,
     theCursor: nil,
     MakeConnection:
       func(passive, address)
       begin
       	local err := ep:Connect(address, nil);
       	if err then
       		begin
       			:PutDataInStatusArea("Couldn't connect..." & NumberStr(err));
       			ep:Dispose();
       			if endpointType = 'adsp then CloseAppleTalk();
       			return;
       		end;
       	
       	connected := true;
       	:PutDataInStatusArea("Connected!!");
       	ConnectButton:Hide();
       	
       	if endpointType = 'ir  or endpointType = 'adsp then
       		frameOptions := kFrame;
       	else
       		frameOptions := nil;
       	
       	:PutDataInStatusArea("Waiting for data...");
       	ep:SetInputSpec(ep.doInput);
       end,
     MLookup:
       func()
       begin
       	if not connected then	// use netchooser to find type: vNBPtype
       		begin
       			GetRoot().NetChooser:OpenNetChooser(nil,
       												"=:" & vNBPtype & "@",
       												nil,
       												self,
       												"Pick this one",
       												"SoupDrink Apps",
       												"SoupDrinks");
       		end;
       end,
     title: "SoupDrink",
     powerOffScript:
       func(what)
       begin
       	if self.connected then return nil else true;
       end,
     NBPaddress: nil,
     vNBPtype: "SoupDrink",
     viewSetupFormScript:
       func()
       begin
       	local b := GetAppParams();
       	constant kMaxWidth := 240;
       	constant kMaxHeight := 336;
       	
       	viewBounds := RelBounds(b.appAreaLeft, b.appAreaTop,
       			Min(b.appAreaWidth, kMaxWidth),
       			Min(b.appAreaHeight, kMaxHeight) );
       	
       	AddPowerOffHandler(self);
       	self.statusMessages := [];
       end,
     ReleaseConnection:
       func()
       begin
       	if connected then
             begin
               ep.nextInputSpec := nil;             // no more input
               ep:SetInputSpec(nil);                // kill the current inputSpec
               ep:Abort();                          // asynchronously abort any pending I/O
       
               // must use a delayed action to dispose
               AddDelayedAction(	func(ep, isAppleTalk)
               					begin
       								ep:Disconnect();
       								ep:Dispose();
       								if isAppleTalk then CloseAppleTalk();
       								ep.appBase.connected := nil;
       							end,
       							[ ep, endpointType = 'adsp ], 2500);
             end;
       end,
     AddName:
       func(f)
       begin	
       	theSoup:AddToDefaultStore(f);
       	BroadcastSoupChange("Names");
       	
       	:PutDataInStatusArea("Frame added...");
          :DisposeConnection();
       end,
     spitMode: nil,
     debug: "DILSample",
     frameOptions: nil
    };

StatusArea :=
    {text: "",
     viewBounds: {left: 10, top: 75, right: 232, bottom: 113},
     viewJustify: 0,
     viewFormat: 67436880,
     viewFont: simpleFont18,
     debug: "StatusArea",
     _proto: @218
    };
AddStepForm(DILSample, StatusArea);
StepDeclare(DILSample, StatusArea, 'StatusArea);



ConnectButton :=
    {text: "Connect",
     buttonClickScript:
       func()
       begin
           if not connected then
       		begin
       			statusMessages := [];		// clear out our array of status messages
       			:SetEndpointType(ConnectionType.clusterValue);
       			if endpointType = 'adsp then
       				:MLookup();							// bring up the chooser; it will call initconnection
       			else
       				:InitConnection(nil);       		// active mode
       		end;
           else
               :Notify(kNotifyAlert, EnsureInternal("SoupDrink"), EnsureInternal("Already connected!"));
       end,
     viewBounds: {left: 12, top: 148, right: 75, bottom: 165},
     viewSetupDoneScript:
       func()
       begin
          :PutDataInStatusArea("Ready to Connect...");
       end,
     debug: "ConnectButton",
     _proto: @226
    };
AddStepForm(DILSample, ConnectButton);
StepDeclare(DILSample, ConnectButton, 'ConnectButton);



DisconnectButton :=
    {text: "Disconnect",
     buttonClickScript:
       func()
       begin
       	if connected then
       		:DisposeConnection();
        	else
       		:PutDataInStatusArea("Ready to connect...");
       end,
     viewBounds: {left: 159, top: 149, right: 230, bottom: 166},
     viewQuitScript:
       func()
       begin
          :ReleaseConnection();
       end,
     debug: "DisconnectButton",
     _proto: @226
    };
AddStepForm(DILSample, DisconnectButton);
StepDeclare(DILSample, DisconnectButton, 'DisconnectButton);



ConnectionType :=
    {viewBounds: {left: 39, top: 191, right: 213, bottom: 254},
     viewSetupDoneScript:
       func()
       begin
       	:SetClusterValue('mnp);
       end,
     debug: "ConnectionType",
     _proto: @203
    };
AddStepForm(DILSample, ConnectionType);
StepDeclare(DILSample, ConnectionType, 'ConnectionType);

serial :=
    {buttonValue: 'serial,
     viewBounds: {left: 7, top: 5, right: 155, bottom: 23},
     text: "Serial",
     debug: "serial",
     _proto: @202
    };
AddStepForm(ConnectionType, serial);



mnp :=
    {buttonValue: 'mnp,
     viewBounds: {left: 7, top: 25, right: 155, bottom: 43},
     text: "Serial with MNP",
     debug: "mnp",
     _proto: @202
    };
AddStepForm(ConnectionType, mnp);



adsp :=
    {buttonValue: 'adsp,
     viewBounds: {left: 7, top: 45, right: 155, bottom: 63},
     text: "AppleTalk (ADSP)",
     debug: "adsp",
     _proto: @202
    };
AddStepForm(ConnectionType, adsp);





SendADSPFrames :=
    {text: "Send frames over ADSP",
     viewBounds: {left: 60, top: 256, right: 233, bottom: 273},
     viewValue: nil,
     debug: "SendADSPFrames",
     _proto: @164
    };
AddStepForm(DILSample, SendADSPFrames);
StepDeclare(DILSample, SendADSPFrames, 'SendADSPFrames);



_view000 :=
    {
     text:
       "(Only enable this with Newton 2.0
         or a 1.x unit with the Dec '95 patch)",
     viewBounds: {left: 71, top: 272, right: 236, bottom: 306},
     viewFont: ROM_fontSystem9,
     viewJustify: 0,
     _proto: @218
    };
AddStepForm(DILSample, _view000);



compileTime :=
    {viewFlags: 513,
     viewFormat: nil,
     viewBounds: {left: 156, top: 0, right: 239, bottom: 22},
     viewClickScript:
       func(unit)
       begin
       if :trackhilite(unit) then
       	begin
       	self.showcompiletime := not self.showcompiletime;
       	:dirty();
       	end;
       
       :hilite(nil);
       end,
     viewDrawScript:
       func()
       begin
       if self.showcompiletime then
       	begin
       	local box := :localbox();
       	:DrawShape(MakeText(datentime(compiletime),box.left,box.top,box.right,box.bottom), {font:viewfont}); 
       	end;
       end,
     viewFont: {family: 'geneva, size: 9, face: 0},
     debug: "compileTime",
     viewClass: 74
    };
AddStepForm(DILSample, compileTime);



status :=
    {text: "Status",
     viewBounds: {left: 18, top: 67, right: 60, bottom: 82},
     viewTransferMode: 0
     ,
     debug: "status",
     _proto: @218
    };
AddStepForm(DILSample, status);
StepDeclare(DILSample, status, 'status);




constant |layout_Main.t| := DILSample;
// End of file Main.t



